#include "protodynamic.h"
#include "luaexport.h"

#ifdef _WIN32
#pragma warning(disable:4355)
#endif


using namespace ::google::protobuf;

ProtoDynamic::ProtoDynamic()
    :importer_(&tree_,this)
{
    tree_.MapPath("/", "/"); // absolute path.
    tree_.MapPath("", "."); // default starts with current directory.
}

void ProtoDynamic::AddError(const std::string& filename,
                        int line, int column,
                        const std::string& message)
{
    std::cerr << filename << '@' << line << ',' << column
              << ':' << message << std::endl;
}

const google::protobuf::FileDescriptor* ProtoDynamic::Import(const std::string& filename)
{
    return importer_.Import(filename);
}

::google::protobuf::Message* ProtoDynamic::CreateMessage(const ::google::protobuf::Descriptor* desc)
{
    return fact_.GetPrototype(desc)->New();
}

void ProtoDynamic::DestroyMessage(::google::protobuf::Message* msg)
{
    delete msg;
}

const ::google::protobuf::DescriptorPool* ProtoDynamic::GetPool()
{
    return importer_.pool();
}

template<class T>
bool IsNull(T* p)
{
    return nullptr==p;
}

void LuaExportProtobuf(lua_State* L,const char* module)
{
    Lua::LuaType<ProtoDynamic>(L,"ProtoDynamic",module)
        .Creator<ProtoDynamic(*)()>("Create")
        .Method("Import",&ProtoDynamic::Import)
        .Method("CreateMessage",&ProtoDynamic::CreateMessage)
        .Method("DestroyMessage",&ProtoDynamic::DestroyMessage)
        .Method("GetPool",&ProtoDynamic::GetPool)
        ;

    Lua::LuaType<MessageLite>(L,"MessageLite",module)
        .Method("ParseFromArray",&MessageLite::ParseFromArray)
        ;
    Lua::LuaType<Message>(L,"Message",module)
        .Super<MessageLite>()
        .Method("GetReflection",&Message::GetReflection)
        .Method("GetDescriptor",&Message::GetDescriptor)
        .Method("New",&Message::New)
        ;

    Lua::LuaType<DescriptorPool>(L,"DescriptorPool",module)
        .Method("FindMessageTypeByName",&DescriptorPool::FindMessageTypeByName)
        .Method("FindEnumTypeByName",&DescriptorPool::FindEnumTypeByName)
        ;

    Lua::LuaType<Descriptor>(L,"Descriptor",module)
        .Method("FindFieldByName",&Descriptor::FindFieldByName)
        ;
    Lua::LuaCFunction(L,"IsNullDesc",&IsNull<Descriptor>,module);

    Lua::LuaType<FileDescriptor>(L,"FileDescriptor",module);
    Lua::LuaCFunction(L,"IsNullFile",&IsNull<FileDescriptor>,module);

    Lua::LuaType<FieldDescriptor>(L,"FieldDescriptor",module);
    Lua::LuaCFunction(L,"IsNullField",&IsNull<FieldDescriptor>,module);

    Lua::LuaType<EnumDescriptor>(L,"EnumDescriptor",module)
        .Method("FindValueByNumber",&EnumDescriptor::FindValueByNumber)
        .Method("FindValueByName",&EnumDescriptor::FindValueByName)
        ;
    Lua::LuaCFunction(L,"IsNullEnum",&IsNull<EnumDescriptor>,module);

    Lua::LuaType<EnumValueDescriptor>(L,"EnumValueDescriptor",module)
        .Method("number",&EnumValueDescriptor::number)
        .Method("name",&EnumValueDescriptor::name)
        ;
    Lua::LuaCFunction(L,"IsNullEnumValue",&IsNull<EnumValueDescriptor>,module);

    Lua::LuaType<Reflection>(L,"Reflection",module)
        .Method("GetInt32",&Reflection::GetInt32)
        .Method("GetInt64",&Reflection::GetInt64)
        .Method("GetUInt32",&Reflection::GetUInt32)
        .Method("GetUInt64",&Reflection::GetUInt64)
        .Method("GetFloat",&Reflection::GetFloat)
        .Method("GetDouble",&Reflection::GetDouble)
        .Method("GetBool",&Reflection::GetBool)
        .Method("GetString",&Reflection::GetString)
        .Method("GetEnum",&Reflection::GetEnum)
        .Method("GetMessage",&Reflection::GetMessage)
        .Method("SetInt32",&Reflection::SetInt32)
        .Method("SetInt64",&Reflection::SetInt64)
        .Method("SetUInt32",&Reflection::SetUInt32)
        .Method("SetUInt64",&Reflection::SetUInt64)
        .Method("SetFloat",&Reflection::SetFloat)
        .Method("SetDouble",&Reflection::SetDouble)
        .Method("SetBool",&Reflection::SetBool)
        .Method("SetString",&Reflection::SetString)
        .Method("SetEnum",&Reflection::SetEnum)
        .Method("MutableMessage",&Reflection::MutableMessage)
        .Method("GetRepeatedInt32",&Reflection::GetRepeatedInt32)
        .Method("GetRepeatedInt64",&Reflection::GetRepeatedInt64)
        .Method("GetRepeatedUInt32",&Reflection::GetRepeatedUInt32)
        .Method("GetRepeatedUInt64",&Reflection::GetRepeatedUInt64)
        .Method("GetRepeatedFloat",&Reflection::GetRepeatedFloat)
        .Method("GetRepeatedDouble",&Reflection::GetRepeatedDouble)
        .Method("GetRepeatedBool",&Reflection::GetRepeatedBool)
        .Method("GetRepeatedString",&Reflection::GetRepeatedString)
        .Method("GetRepeatedEnum",&Reflection::GetRepeatedEnum)
        .Method("GetRepeatedMessage",&Reflection::GetRepeatedMessage)
        .Method("SetRepeatedInt32",&Reflection::SetRepeatedInt32)
        .Method("SetRepeatedInt64",&Reflection::SetRepeatedInt64)
        .Method("SetRepeatedUInt32",&Reflection::SetRepeatedUInt32)
        .Method("SetRepeatedUInt64",&Reflection::SetRepeatedUInt64)
        .Method("SetRepeatedFloat",&Reflection::SetRepeatedFloat)
        .Method("SetRepeatedDouble",&Reflection::SetRepeatedDouble)
        .Method("SetRepeatedBool",&Reflection::SetRepeatedBool)
        .Method("SetRepeatedString",&Reflection::SetRepeatedString)
        .Method("SetRepeatedEnum",&Reflection::SetRepeatedEnum)
        .Method("MutableRepeatedMessage",&Reflection::MutableRepeatedMessage)
        .Method("FieldSize",&Reflection::FieldSize)
        .Method("HasField",&Reflection::HasField)
        .Method("ClearField",&Reflection::ClearField)
        .Method("AddInt32",&Reflection::AddInt32)
        .Method("AddInt64",&Reflection::AddInt64)
        .Method("AddUInt32",&Reflection::AddUInt32)
        .Method("AddUInt64",&Reflection::AddUInt64)
        .Method("AddFloat",&Reflection::AddFloat)
        .Method("AddDouble",&Reflection::AddDouble)
        .Method("AddBool",&Reflection::AddBool)
        .Method("AddString",&Reflection::AddString)
        .Method("AddEnum",&Reflection::AddEnum)
        .Method("AddMessage",&Reflection::AddMessage)
        ;
}
